﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Configuration;
using ats_KNNs;

namespace IGuess.KNN
{
    public class KNNClassifier : Classifier
    {

        #region Atributes

        private int numberOfWords;
        private System.Resources.ResourceManager RM = new System.Resources.ResourceManager("IGuess.GlobalResources", System.Reflection.Assembly.GetExecutingAssembly());
        private Hashtable statsEN = new Hashtable();
        private Hashtable statsFR = new Hashtable();
        private Hashtable statsDE = new Hashtable();
        private Hashtable euclideanDistances = new Hashtable();
        private string result;
        private string[] languages = { "en", "fr", "de" };
        private double mean;
        KNNs calc;

        #endregion

        #region Properties

        public int NumberOfWords
        {
            get { return this.numberOfWords; }
            set { this.numberOfWords = value; }
        }

        public double Mean
        {
            get { return this.mean; }
            set { this.mean = value; }
        }

        public string Result
        {
            get { return this.result; }
            set { this.result = value; }
        }

        public string[] Languages
        {
            get { return this.languages; }
            set { this.languages = value; }
        }

        public Hashtable StatsEN
        {
            get { return this.statsEN; }
            set { this.statsEN = value; }
        }

        public Hashtable StatsFR
        {
            get { return this.statsFR; }
            set { this.statsFR = value; }
        }

        public Hashtable StatsDE
        {
            get { return this.statsDE; }
            set { this.statsDE = value; }
        }

        public Hashtable EuclideanDistances
        {
            get { return this.euclideanDistances; }
            set { this.euclideanDistances = value; }
        }

        #endregion
        
        #region Constructors
        public KNNClassifier(string text)
        {
            this.text = text;
            this.NumberOfWords = text.Length;
            calc = new KNNs();
        }

        public KNNClassifier() {

            calc = new KNNs();

        }

        #endregion

        #region Methods

        public override string Classify()
        {

            string[] languages = { "en", "fr", "de"};
            double[] currentAnalizedTextMeans = { this.CountStringOccurrences(this.text, "a") / this.text.Length, this.CountStringOccurrences(this.text, "e") / this.text.Length, this.CountStringOccurrences(this.text, "i") / this.text.Length, this.CountStringOccurrences(this.text, "n") / this.text.Length, this.CountStringOccurrences(this.text, "o") / this.text.Length, this.CountStringOccurrences(this.text, "r") / this.text.Length, this.CountStringOccurrences(this.text, "s") / this.text.Length, this.CountStringOccurrences(this.text, "t") / this.text.Length };
            
            foreach (string language in languages)
            {

                if (File.Exists(ConfigurationManager.AppSettings[language + "_StatsFile"]))
                {
                    using (System.IO.StreamReader sr = new System.IO.StreamReader(ConfigurationManager.AppSettings[language + "_StatsFile"]))
                    {

                        string line;
                        int textCounter = 1;

                        while ((line = sr.ReadLine()) != null)
                        {

                            string[] token = line.Split();
                            double[] currentTextMeans = new double[token.Length];
                            int i = 0;

                            foreach (string value in token)
                            {

                                double.TryParse(value, out currentTextMeans[i]);

                                i++;

                            }
                            double distance = this.calc.EuclideanDistance(currentAnalizedTextMeans, currentTextMeans);

                            this.euclideanDistances.Add(language + textCounter, distance);

                            textCounter++;
                        }

                    }

                }

            }

            var result = new List<DictionaryEntry>(this.euclideanDistances.Count);
            foreach (DictionaryEntry entry in this.euclideanDistances)
            {
                result.Add(entry);
            }
            result.Sort(
                (x, y) =>
                {
                    IComparable comparable = x.Value as IComparable;
                    if (comparable != null)
                    {
                        return comparable.CompareTo(y.Value);
                    }
                    return 0;
                });

            Hashtable mostKN = new Hashtable();

            int topFour = 0;

            foreach (DictionaryEntry entry in result)
            {

                if (topFour < 4)
                {

                    mostKN.Add(entry.Key, entry.Value);

                }

                topFour++;

            }

            int frNeighbours = 0;
            int enNeighbours = 0;
            int deNeighbours = 0;

            foreach (DictionaryEntry entry in mostKN)
            {

                if (entry.Key.ToString().Contains("en"))
                    enNeighbours++;

                else if (entry.Key.ToString().Contains("fr"))
                    frNeighbours++;

                else if (entry.Key.ToString().Contains("de"))
                    deNeighbours++;

            }

            this.Mean = (currentAnalizedTextMeans[0] + currentAnalizedTextMeans[1] + currentAnalizedTextMeans[2] + currentAnalizedTextMeans[3] + currentAnalizedTextMeans[4] + currentAnalizedTextMeans[5] + currentAnalizedTextMeans[6] + currentAnalizedTextMeans[7]) / 8;

            if (deNeighbours > enNeighbours && deNeighbours > frNeighbours)
            {
                this.Result = "DE";
                return RM.GetString("FrequenceAlgorithmResult") + RM.GetString("DETextLanguage") + ".";
            }
            else if (enNeighbours > frNeighbours && enNeighbours > deNeighbours)
            {
                this.Result = "EN";
                return RM.GetString("FrequenceAlgorithmResult") + RM.GetString("ENTextLanguage") + ".";
            }
            else if (frNeighbours > enNeighbours && frNeighbours > deNeighbours)
            {
                this.Result = "FR";
                return RM.GetString("FrequenceAlgorithmResult") + RM.GetString("FRTextLanguage") + ".";
            }
            else
            {
                this.Result = "NA";
                return RM.GetString("FrequenceAlgorithmUnknownResult");
            }

        }

        private double CountStringOccurrences(string text, string pattern)
        {
            // Loop through all instances of the string 'text'.
            double count = 0;
            int i = 0;
            while ((i = text.ToLower().IndexOf(pattern.ToLower(), i)) != -1)
            {
                i += pattern.Length;
                count++;
            }
            return count;
        }

        #endregion
    }
}
